package com.shitou.shitousearch.service;


import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.shitou.shitoucommons.exception.RRException;
import com.shitou.shitoucommons.utils.RespBean;
import com.shitou.shitoucommons.vo.Attrs;
import com.shitou.shitoucommons.vo.SearchResponseVo;
import com.shitou.shitousearch.vo.RespVo;
import com.shitou.shitousearch.vo.SearchRespAttrVo;
import com.shitou.shitousearch.vo.SearchVo;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;


/**
 * class detail:
 *
 * @author 石头人
 */
@SuppressWarnings("all")
@Service
public class SearchService {

    @Autowired
    @Qualifier("restHighLevelClient")
    private RestHighLevelClient restHighLevelClient;

    /**
     * 搜索
     *
     * @param searchVo
     * @return
     */
    public RespBean search(SearchVo searchVo) {

        SearchRequest searchRequest = queryBuildDSL(searchVo);
        if (searchRequest == null) {
            // 关键字为空，默认为查询全部，并将数据放入缓存中，缓存时间为10分钟

            // 或者一律返回nullxsx
            searchRequest = new SearchRequest("shitou");
            searchRequest.types("product");
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//            throw new RRException("请输入关键字");
            return RespBean.success().put("data", null);
        }
        SearchResponse response = null;
        RespVo respVo = null;
        // 获得数据并聚合所有的数据
        try {
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            respVo = fetchSearchResponseVO(response);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RRException("查询数据失败");
        }
        List<SearchRespAttrVo> attrs = respVo.getAttrs();
        Set<SearchRespAttrVo> set = new HashSet<>();
        set.addAll(attrs);
        respVo.setAttrs(new ArrayList<SearchRespAttrVo>(set));

        return RespBean.success().put("data", respVo);
    }


    public RespVo fetchSearchResponseVO(SearchResponse response) {
        RespVo resp = new RespVo();
        //获取总记录数
        SearchHits hits = response.getHits();

        // 获取里面的product
        List<SearchResponseVo> products = new ArrayList<>();
        for (SearchHit subHit : hits.getHits()) {
            SearchResponseVo responseVo = JSON.parseObject(subHit.getSourceAsString(), SearchResponseVo.class);
            String title = responseVo.getSpuTitle(),
                    detail = responseVo.getSpuDetail(), nickname = responseVo.getNickname();
            // 设置高亮属性
            if (subHit.getHighlightFields().get("spuTitle") != null) {
                title = subHit.getHighlightFields().get("spuTitle").getFragments()[0].toString();
            }
            if (subHit.getHighlightFields().get("spuDetail") != null) {
                detail = subHit.getHighlightFields().get("spuDetail").getFragments()[0].toString();
            }
            if (subHit.getHighlightFields().get("nickname") != null) {
                nickname = subHit.getHighlightFields().get("nickname").getFragments()[0].toString();
            }

            responseVo.setSpuTitle(title);
            responseVo.setSpuDetail(detail);
            responseVo.setNickname(nickname);
            products.add(responseVo);
        }
        resp.setProducts(products);

        resp.setTotal(hits.totalHits);   // 设置数据总数


        // 获取聚合桶集合
        Map<String, Aggregation> aggregationMap = response.getAggregations().asMap();
        // 解析品牌
        ParsedTerms brandIdAgg = (ParsedTerms) aggregationMap.get("brandIdAgg");
        if (brandIdAgg.getBuckets().size() > 0) {
            List<String> brandValue = brandIdAgg.getBuckets().stream().map(bocket -> {
                Map<String, String> map = new HashMap<>();

                String id = bocket.getKeyAsString();   // brandId聚合中，里面的id就是brandId
                map.put("id", id);
                // 获得gai id 下面的子聚合，也就是名字品牌名称
                Map<String, Aggregation> subAggMap = bocket.getAggregations().asMap();
                ParsedStringTerms brandAgg = (ParsedStringTerms) subAggMap.get("brandAgg");
                ParsedStringTerms brandLogAgg = (ParsedStringTerms) subAggMap.get("brandLogAgg");

                String brandName = brandAgg.getBuckets().get(0).getKeyAsString();
                String brandLog = brandLogAgg.getBuckets().get(0).getKeyAsString();
                map.put("name", brandName);
                map.put("brandLog", brandLog);

                return JSONUtils.toJSONString(map);
            }).collect(Collectors.toList());

            SearchRespAttrVo brand = new SearchRespAttrVo();
            brand.setAttrName("品牌");
            brand.setValue(brandValue);
            resp.setBrand(brand);   // 添加到返回的数据中
        }

        // 解析分类
        ParsedLongTerms categoryIdAgg = (ParsedLongTerms) aggregationMap.get("categoryIdAgg");
        if (categoryIdAgg.getBuckets().size() > 0) {
            List<String> categoryValue = categoryIdAgg.getBuckets().stream().map(bocket -> {
                Map<String, String> map = new HashMap<>();

                String id = bocket.getKeyAsString();   // brandId聚合中，里面的id就是brandId
                map.put("id", id);
                // 获得gai id 下面的子聚合，也就是名字品牌名称
                Map<String, Aggregation> subAggMap = bocket.getAggregations().asMap();
                ParsedStringTerms brandAgg = (ParsedStringTerms) subAggMap.get("categoryAgg");
                if (brandAgg != null) {
                    String brandName = brandAgg.getBuckets().get(0).getKeyAsString();
                    map.put("name", brandName);
                }
                return JSONUtils.toJSONString(map);
            }).collect(Collectors.toList());
            SearchRespAttrVo category = new SearchRespAttrVo();
            category.setAttrName("分类");
            category.setValue(categoryValue);
            resp.setCategory(category);   // 添加到返回的数据中
        }

        // 解析属性Agg
        ParsedNested attrAgg = (ParsedNested) aggregationMap.get("attrAgg");
        // 规格参数id聚合对象
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        if (attrIdAgg.getBuckets().size() > 0) {
            List<SearchRespAttrVo> attrs = attrIdAgg.getBuckets().stream().map(bucket -> {
                SearchRespAttrVo searchAttrVO = new SearchRespAttrVo();
                // 设置规格参数id
                searchAttrVO.setAttrId(bucket.getKeyAsNumber().longValue());

                // 设置规格参数名
                List<? extends Terms.Bucket> attrNameAgg = ((ParsedStringTerms)bucket.getAggregations().get("attrNameAgg")).getBuckets();
                if ( attrNameAgg != null && attrNameAgg.size() > 0 ){
                    searchAttrVO.setAttrName(attrNameAgg.get(0).getKeyAsString());
                }
                // 设置规格参数值的列表
                List<? extends Terms.Bucket> attrValueBuckets = ((ParsedStringTerms) bucket.getAggregations().get("attrValueAgg")).getBuckets();
                if ( attrValueBuckets != null && attrValueBuckets.size() > 0){
                    List<String> values = attrValueBuckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
                    searchAttrVO.setValue(values);
                }

                return searchAttrVO;
            }).collect(Collectors.toList());
            resp.setAttrs(attrs);
        }
        return resp;
    }


    /**
     * 创建文档查询层级 DSL
     *
     * @param searchVo
     * @return
     */
    private SearchRequest queryBuildDSL(SearchVo searchVo) {
        String keyword = searchVo.getKeyword();
        if (StringUtils.isBlank(keyword)) {
            return null;
        }

        // 查询条件 构建器
        SearchSourceBuilder searchBuilder = new SearchSourceBuilder();
        // 构建查询条件和过滤条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 关键字在 spuTitle和spuDetail中查询
        boolQuery.should(QueryBuilders.matchQuery("spuTitle", keyword));
//        boolQuery.should(QueryBuilders.matchQuery("spuDetail", keyword));
        boolQuery.should(QueryBuilders.matchQuery("nickname", keyword));
        boolQuery.should(QueryBuilders.matchQuery("brand", keyword));
        boolQuery.should(QueryBuilders.matchQuery("category", keyword));

        // 三个中必须得瞒住条件数要 >= 1
        boolQuery.minimumShouldMatch(1);



        // 品牌过滤
        String brand = searchVo.getBrand();
        Long brandId = searchVo.getBrandId();
        if (brand != null && !brand.equals("")) {
            boolQuery.filter(QueryBuilders.termQuery("brand", brand));
        }
        if (brandId != null && brandId.equals(0L)) {
            boolQuery.filter(QueryBuilders.termQuery("brandId", brandId));
        }

        // 过滤状态为 0 的商品,得到状态为1 的商品
        boolQuery.filter(QueryBuilders.termQuery("status", 1));

        // 分类过滤
        String category = searchVo.getCategory();
        Long categoryId = searchVo.getCategoryId();
        if (category != null && !category.equals("")) {
            boolQuery.filter(QueryBuilders.termQuery("category", category));
        }
        if (categoryId != null && categoryId.equals(0L)) {
            boolQuery.filter(QueryBuilders.termQuery("categoryId", categoryId));
        }


        // 构建属性嵌套过滤
        List<Attrs> attrsList = searchVo.getAttrs();
        if (attrsList != null && attrsList.size() > 0) {
            for (Attrs attr : attrsList) {
                BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                BoolQueryBuilder subBoolQueryBuilder = QueryBuilders.boolQuery();

                subBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", attr.getAttrId()));
                subBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrValue", attr.getAttrValue()));

                // 添加嵌套查询
                boolQueryBuilder.must(QueryBuilders.nestedQuery("attrs", subBoolQueryBuilder, ScoreMode.None));
                // 将上面构建的嵌套查询加入到大的查询当中
                boolQuery.filter(boolQueryBuilder);
            }
        }

        // 价格区间过滤
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("spuPrice");
        Integer priceFrom = searchVo.getPriceFrom();
        Integer priceTo = searchVo.getPriceTo();

        if (priceFrom != null && priceFrom > 0 && priceTo != null && priceTo >= priceFrom) {
            rangeQuery.gte(priceFrom);
            rangeQuery.lte(priceTo);
        }
        boolQuery.filter(rangeQuery);

        // 查询
        searchBuilder.query(boolQuery);

        Integer pageNum = searchVo.getPageNum();
        Integer pageSize = searchVo.getPageSize();
        /**
         * 从上一页的最后一个开始进行计算,
         * 因为pageNum默认设置了为1，如果没有传值的话
         */
        searchBuilder.from((pageNum - 1) * pageSize);
        searchBuilder.size(pageSize);


        // 排序
        String order = searchVo.getOrder();
        if (StringUtils.isNotEmpty(order)) {
            String filed = null;
            String[] split = order.split(":");
            if (split != null && split.length == 2) {
                String field = "";
                switch (split[0]) {
                    case "0": field = "none";
                        break;
                    case "1":
                        field = "spuPrice";
                        break;
                }
                if ( !field.equals("none") ){
                    searchBuilder.sort(field, StringUtils.equals(split[1], "asc") ? SortOrder.ASC : SortOrder.DESC);
                }
            }
        }

        String preTags = "<span style='color: #ff4000; font-weight: 700; text-decoration: underline;'>";
        String postTags = "</span>";

        // 高亮
        searchBuilder.highlighter(new HighlightBuilder()
                .field("spuTitle").preTags(preTags).postTags(postTags)
                .field("spuDetail").preTags(preTags).postTags(postTags)
                .field("nickname").preTags(preTags).postTags(postTags)
        );

        // 构建聚合查询
        searchBuilder.aggregation(AggregationBuilders.terms("brandIdAgg").field("brandId")
                .subAggregation(AggregationBuilders.terms("brandAgg").field("brand"))
                .subAggregation(AggregationBuilders.terms("brandLogAgg").field("brandLog")));

        // 分类聚合
        searchBuilder.aggregation(AggregationBuilders.terms("categoryIdAgg").field("categoryId").subAggregation(
                AggregationBuilders.terms("categoryAgg").field("category")));
        // 搜索的属性聚合
        searchBuilder.aggregation(AggregationBuilders.nested("attrAgg", "attrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue"))));

        // 结果集过滤
        searchBuilder.fetchSource(new String[]{"spuId", "memberId", "nickname", "spuTitle", "spuDetail", "spuImage", "spuPrice", "brand", "brandId", "brandLog"}, null);

        // 查询参数
        SearchRequest searchRequest = new SearchRequest("muyang");
        searchRequest.types("product");
        searchRequest.source(searchBuilder);
        return searchRequest;
    }

}
